<?php

class Piles {

    public $drawDeck;
    public $playArea;
    public $playHand;
    public $discardDeck;
    public $nativeVillage;
    public $island;
    public $haven;
    public $durationAffects;
    public $playedCards;
    public $gainedCards;
    public $boughtCards;
    public $contrabandCards;
    public $cardsStack;
    public $cardsInfluence;
    public $horseTraders;
    public $reactions;

    public function __construct(
    $drawDeck = array(), $playArea = array(), $playHand = array(), $discardDeck = null, $cardsStack = array(), $nativeVillage = array(), $island = array(), $haven = array(), $durationAffects = array(), $influence = array(), $playedCards = array(), $gainedCards = array(), $boughtCards = array(), $contrabandCards = array(), $horseTraders = array(), $reactions = array()
    ) {

        $this->drawDeck = $drawDeck;
        $this->playArea = $playArea;
        $this->playHand = $playHand;
        $this->playHand = $this->sortPile($this->playHand);
        $this->cardsStack = $cardsStack;
        $this->nativeVillage = $nativeVillage;
        $this->island = $island;
        $this->haven = $haven;
        $this->durationAffects = $durationAffects;
        $this->cardsInfluence = $influence;
        $this->playedCards = $playedCards;
        $this->gainedCards = $gainedCards;
        $this->boughtCards = $boughtCards;
        $this->contrabandCards = $contrabandCards;
        $this->horseTraders = $horseTraders;
        $this->reactions = $reactions;
        $this->discardDeck = $discardDeck;
    }

    public function setDeckWithDarkAges() {
        $copper = new Copper();
        $hovel = new Hovel();
        $necropolis = new Necropolis();
        $overGrownEstate = new OvergrownEstate();
        $this->discardDeck = array($copper, $copper, $copper, $copper, $copper, $copper, $copper, $hovel, $necropolis, $overGrownEstate);
    }

    public function setDeck() {
        $copper = new Copper();
        $estate = new Estate();
        $this->discardDeck = array($copper, $copper, $copper, $copper, $copper, $copper, $copper, $estate, $estate, $estate);
    }

    public function setDeckForTest() {

        $copper = new Copper();
        $estate = new Estate();
        $this->discardDeck = array($copper, $copper, $copper, $copper, $copper, $copper, $copper, $estate, $estate, $estate);


        //$a = new Gold();
        //$b = new Province();
        //$c = new Remodel();
        //$this->discardDeck = array($a, $a, $a,$a ,$b, $a);
    }

    public function getPilesData(Game $game, $data) {
        #$data['drawDeck'] = getPileData($game, $this->drawDeck);
        $data['drawDeck'] = sizeof(getPileData($game, $this->drawDeck));
        $data['playHand'] = getPileData($game, $this->playHand);
        
        $data['playArea'] = getPileData($game, $game->getPlayArea());
        //$data['playArea'] = getPileData($game, $this->playArea);
        #$data['discardDeck'] = getPileData($game, $this->discardDeck);
        $data['discardDeck'] = sizeof(getPileData($game, $this->discardDeck));
        $data['cardsStack'] = getQuantityPileData($game, $this->cardsStack);
        $data['cardsInfluence'] = getPileData($game, $this->cardsInfluence);
        $data['haven'] = getPileData($game, $this->haven);
        $data['island'] = getPileData($game, $this->island);
        $data['nativeVillage'] = getPileData($game, $this->nativeVillage);
        $data['durationAffects'] = getQuantityPileData($game, $this->durationAffects);
        $data['playedcards'] = getPileData($game, $this->playedCards);
        $data['gainedcards'] = getPileData($game, $this->gainedCards);
        $data['boughtcards'] = getPileData($game, $this->boughtCards);
        $data['contrabandcards'] = getPileData($game, $this->contrabandCards);
        $data['horsetraders'] = getPileData($game, $this->horseTraders);
        $data['reactions'] = getPileData($game, $this->reactions);
        return $data;
    }

    public function save($gid, $name) {
        $this->savePileHelper($gid, $name, 'drawdeck', $this->drawDeck);
        $this->savePileHelper($gid, $name, 'playarea', $this->playArea);
        $this->savePileHelper($gid, $name, 'playhand', $this->playHand);
        $this->savePileHelper($gid, $name, 'discarddeck', $this->discardDeck);
        $this->saveQuantityPileHelper($gid, $name, 'cardsstack', $this->cardsStack);
        $this->savePileHelper($gid, $name, 'nativevillage', $this->nativeVillage);
        $this->savePileHelper($gid, $name, 'island', $this->island);
        $this->savePileHelper($gid, $name, 'haven', $this->haven);
        $this->saveQuantityPileHelper($gid, $name, 'durationaffects', $this->durationAffects);
        $this->savePileHelper($gid, $name, 'influence', $this->cardsInfluence);
        $this->savePileHelper($gid, $name, 'playedcards', $this->playedCards);
        $this->savePileHelper($gid, $name, 'gainedcards', $this->gainedCards);
        $this->savePileHelper($gid, $name, 'boughtcards', $this->boughtCards);
        $this->savePileHelper($gid, $name, 'contrabandcards', $this->contrabandCards);
        $this->savePileHelper($gid, $name, 'horsetraders', $this->horseTraders);
        $this->savePileHelper($gid, $name, 'reactions', $this->reactions);
    }

    private function saveQuantityPileHelper($gid, $name, $type, $pile) {
        $db = DBUtils::Create();
        $cs = "";
        foreach ($pile as $card) {
            $cs .= $card['card']->getKey() . "~" . $card['quantity'] . "|";
        }
        $cs = substr($cs, 0, strlen($cs) - 1);
        $db->savePile($gid, $name, $type, $cs);
    }

    private function savePileHelper($gid, $name, $type, $pile) {
        $db = DBUtils::Create();
        $cs = "";
        foreach ($pile as $card) {
            $cs .= $card->getKey() . "|";
        }
        $cs = substr($cs, 0, strlen($cs) - 1);
        $db->savePile($gid, $name, $type, $cs);
    }

    public function endTurn(Game $game) {
        $this->discardDeck = array_merge($this->playHand, $this->playArea, $this->discardDeck);
        $this->playArea = array();
        $this->playHand = array();
        $this->cardsStack = array();
        $this->contrabandCards = array();
        $this->reactions = array();
        $this->cardsInfluence = array();
    }

    public function startTurn(Game $game, $name) {
        //playing the duration cards
        for ($i = 0; $i < sizeof($this->durationAffects); $i+=1) {
            $card_arr = $this->durationAffects[$i];
            $card = $card_arr['card'];
            $quantity = $card_arr['quantity'];
            $newQuantity = $card->startTurn($game, $quantity);
            $this->durationAffects[$i]['quantity'] = $newQuantity;
        }
        //activating the horse traders pile
        $this->gainHorseTradersCards($game, $name);
        $this->horseTraders = array();
        //clearing the played, gained and bought cards this turn
        $this->playedCards = array();
        $this->gainedCards = array();
        $this->boughtCards = array();
    }

    public function playTreasures($game) {
        $i = 0;
        for ($i = 0; $i < sizeof($this->playHand); $i += 1) {
            $card = $this->playHand[$i];
            if ($card instanceof iTreasure) {
                array_splice($this->playHand, $i, 1);
                array_unshift($this->playArea, $card);
                //we deleted a card, we need to fix the index
                $i -=1;
            }
        }
        $this->playHand = $this->sortPile($this->playHand);
    }
    
    public function getPlayArea() {
        return $this->playArea;
    }

    public function hasMoreCards() {
        if ((sizeof($this->discardDeck) > 0) || (sizeof($this->drawDeck) > 0)) {
            return true;
        }
        return false;
    }

    private function sortPile($pile) {
        $temp = array();
        foreach ($pile as $card) {
            $ck = $card->getKey();
            if (!array_key_exists($ck, $temp)) {
                $temp[$ck] = array();
            }
            $temp[$ck][] = $card;
        }
        ksort($temp);
        $res = array();
        foreach ($temp as $c_ar) {
            $res = array_merge($res, $c_ar);
        }

        return $res;
    }

    public function drawCards($game, $name, $num, $printMessage = true) {
        $drawed_cards = array();
        $drawDeckNum = min($num, sizeof($this->drawDeck));
        for ($i = 0; $i < $drawDeckNum; $i+=1) {
            $c = array_shift($this->drawDeck);
            array_unshift($this->playHand, $c);
            $drawed_cards[] = $c;
        }

        $num = $num - $drawDeckNum;
        if ($num > 0) {
            $drawDeckNum = $this->doReshuffleIfRequired($game, $name, $num, $printMessage);
            for ($i = 0; $i < $drawDeckNum; $i+=1) {
                $c = array_shift($this->drawDeck);
                array_unshift($this->playHand, $c);
                $drawed_cards[] = $c;
            }
        }
        $drawed_cards = array_reverse($drawed_cards);
        $this->playHand = $this->sortPile($this->playHand);
        return $drawed_cards;
    }

    public function peekCardFromDrawDeck($game, $name) {

        $num = $this->doReshuffleIfRequired($game, $name, 1);
        if ($num > 0) {
            $c = array_shift($this->drawDeck);
            array_unshift($this->drawDeck, $c);
            return $c;
        }
        return null;
    }

    //this method will remove a card from the draw deck,
    //it is your resposability to put it back after wherever it fits
    public function removeCardsFromDrawDeck($game, $name, $num) {

        $cards = array();
        $drawDeckNum = min($num, sizeof($this->drawDeck));
        for ($i = 0; $i < $drawDeckNum; $i+=1) {
            $c = array_shift($this->drawDeck);
            $cards[] = $c;
        }

        $num = $num - $drawDeckNum;
        if ($num > 0) {
            $drawDeckNum = $this->doReshuffleIfRequired($game, $name, $num);
            for ($i = 0; $i < $drawDeckNum; $i+=1) {
                $c = array_shift($this->drawDeck);
                $cards[] = $c;
            }
        }
        $cards = array_reverse($cards);
        return $cards;
    }

    public function addCardToPlayed($card) {
        array_push($this->playedCards, $card);
    }

    public function addCardToGained($card) {
        array_unshift($this->gainedCards, $card);
    }

    public function addCardToBought($card) {
        array_unshift($this->boughtCards, $card);
    }

    public function removeBottomDrawDeckCard($game, $name) {
        $num = $this->doReshuffleIfRequired($game, $name, 1);
        if ($num == 0)
            return null;
        return array_pop($this->drawDeck);
    }

    public function putCardInPlayHand($card) {
        array_unshift($this->playHand, $card);
        $this->playHand = $this->sortPile($this->playHand);
        return $card;
    }

    public function pushCardToDurationAffects($card, $quantity, $isRealCard) {
        $card_arr = array('card' => $card, 'quantity' => $quantity * $isRealCard);
        return array_push($this->durationAffects, $card_arr);
    }

    public function putCardOnBottomOfDrawDeck($card) {
        return array_push($this->drawDeck, $card);
    }

    public function contrabandCard($card) {
        return array_unshift($this->contrabandCards, $card);
    }

    public function discardPlayHand() {
        $this->discardDeck = array_merge($this->playHand, $this->discardDeck);
        $this->playHand = array();
    }

    public function discardTempDiscardCards($discardCards) {
        $this->discardDeck = array_merge($discardCards, $this->discardDeck);
        return true;
    }

    public function putCardsInPlayHand($cards) {
        $this->playHand = array_merge($cards, $this->playHand);
        $this->playHand = $this->sortPile($this->playHand);
        return true;
        ;
    }

    public function removeCardFromPlayHand($card) {
        list($this->playHand, $card) = $this->removeCardFromPile($card, $this->playHand);
        $this->playHand = $this->sortPile($this->playHand);
        return $card;
    }

    public function removeCardFromPlayArea($card) {
        list($this->playArea, $card) = $this->removeCardFromPile($card, $this->playArea);
        return $card;
    }

    public function removeCardFromDiscardPile($card) {
        list($this->discardDeck, $card) = $this->removeCardFromPile($card, $this->discardDeck);
        return $card;
    }

    public function removeCardFromPlayedCards($card) {
        list($this->playedCards, $card) = $this->removeCardFromPile($card, $this->playedCards);
        return $card;
    }

    private function removeCardFromPile($card, $pile) {

        $i = 0;
        $removed_card = null;
        foreach ($pile as $c) {
            if ($c->getKey() == $card->getKey()) {
                array_splice($pile, $i, 1);
                $removed_card = $c;
                break;
            }
            $i+=1;
        }
        return array($pile, $removed_card);
    }

    public function discardCardsFromDrawDeck($game, $name, $num) {
        $num = $this->doReshuffleIfRequired($game, $name, $num);
        $discarded_cards = array();
        for ($i = 0; $i < $num; $i+=1) {
            $c = array_shift($this->drawDeck);
            array_unshift($this->discardDeck, $c);
            $discarded_cards[] = $c;
        }
        return $discarded_cards;
    }

    public function putCardInDurationDeck($card) {
        return $this->piles->putCardInDurationDeck($card);
    }

    public function discardCardsFromDurationDeck() {
        $tmpDiscard = array();
        $tmpDuration = array();
        foreach ($this->durationAffects as $card_arr) {
            $quantity = $card_arr['quantity'];
            if ($quantity == RETURN_DURATION_TO_DISCARD) {
                $card = $card_arr['card'];
                $tmpDiscard[] = $card;
            } else if ($quantity == DELETE_VIRTUAL_DURATION_CARD) {
                //do nothing here since this is a virtural card created probably by TR or KC.
            } else {
                $tmpDuration[] = $card_arr;
            }
        }

        $this->discardDeck = array_merge($tmpDiscard, $this->discardDeck);
        $this->durationAffects = $tmpDuration;
        return true;
    }

    public function getCardInHand($card) {
        for ($i = 0; $i < sizeof($this->playHand); $i+=1) {
            $c = $this->playHand[$i];
            if ($c->getKey() == $card->getKey()) {
                return $c;
            }
        }
        return null;
    }

    public function putCardInIslandDeck($card) {
        return array_unshift($this->island, $card);
    }

    public function putCardInHavenDeck($card) {
        return array_unshift($this->haven, $card);
    }

    public function PutCardsFromHavenDeckToHand() {
		$temp = $this->haven;
        $this->playHand = array_merge($this->playHand, $this->haven);
        $this->playHand = $this->sortPile($this->playHand);
        $this->haven = array();
        return $temp;
    }

    public function putCardInNativeVillageDeck($card) {
        return array_unshift($this->nativeVillage, $card);
    }

    public function putCardInHorseTradersDeck($card) {
        return array_unshift($this->horseTraders, $card);
    }

    public function PutCardsFromNativeVillageDeckToHand() {
        $this->playHand = array_merge($this->playHand, $this->nativeVillage);
        $this->playHand = $this->sortPile($this->playHand);
        $temp = $this->nativeVillage;
        $this->nativeVillage = array();
        return $temp;
    }

    public function gainHorseTradersCards($game, $name) {
        if (sizeof($this->horseTraders) > 0) {
            $ht = new HorseTraders();
            $drawed_cards = $this->drawCards($game, $name, sizeof($this->horseTraders));
            $this->playHand = array_merge($this->horseTraders, $this->playHand);
            $this->playHand = $this->sortPile($this->playHand);
            ScreenDisplay::addScreenMessage($game, true, "activate " . nicePrint($this->horseTraders) . " and draws " . nicePrint($drawed_cards), "activates " . nicePrint($this->horseTraders) . " and draws " . sizeof($drawed_cards) . " cards", $name);
        }
    }

    public function getAllCards() {
        $realDuration = array();
        foreach ($this->durationAffects as $card_arr) {
            if ($card_arr['quantity'] > 0) {
                $realDuration[] = $card_arr['card'];
            }
        }
        return array_merge($this->discardDeck, $this->drawDeck, $this->playHand, $this->playArea, $this->island, $this->nativeVillage, $this->haven, $realDuration);
    }

    public function shuffleDrawDeck() {
        shuffle($this->drawDeck);
    }

    //does reshuffle if required and return the number of cards it is possible to 'work' with,
    //usually it will return the number of cards given, in very rare cases when the deck is 
    //very thin it will return the number of cards it contains.
    private function doReshuffleIfRequired($game, $name, $num, $printMessage = true) {

        //we check if this triggers reshuffle
        if (sizeof($this->drawDeck) < $num) {
            if ($printMessage && sizeof($this->discardDeck) > 0) {
                ScreenDisplay::addScreenMessage($game, true, " reshuffle", "reshuffles");
            }

            shuffle($this->discardDeck);

            #checking if there are any cards needed to be drawn
            foreach ($this->discardDeck as $dis_card) {
                if ($dis_card instanceof Stash) {
                    $this->discardDeck = $dis_card->setDiscardDeck($game, $name);
                    break;
                }
            }

            $this->drawDeck = array_merge($this->drawDeck, $this->discardDeck);
            $this->discardDeck = array();
            //now we check after possible reshuffle if we have enough cards in drawpile
            if (sizeof($this->drawDeck) < $num) {
                $num = sizeof($this->drawDeck);
            }
        }
        return $num;
    }

}

?>